package base;

import java.util.ArrayList;
import java.util.Iterator;

/**
 *
 * @author TueNV
 */
public class TrackNode {
    private Type type;  //Type of this track
    private String address; //address of this track
    private String host;    //host ip address    
    private TrackNode parent; //Track Node parent    
    private ArrayList<TrackNode> children; //List all children
    private Rule rule; //Track control by rule
    private int activateBranch; //current actiavte branch in sequential rule
    private Queue<TrackNode> suspendedBranch;
    private Echo echo; //current echo merged from chile    
    private int remainingEcho; //remaining echo need receive from track child
    private String enclosedWave; //Enclosed wave in rule
    private String suspendedTail; //Suspended wave tail        
    private WaveEnvironment waveEnv; //Wave environment of current node     
    /**
     * Ham khoi tao mac dinh
     */
    public TrackNode() {
        type = Type.PROP;
        children = new ArrayList<TrackNode>();
        suspendedBranch = new Queue<TrackNode>();
        echo = Echo.NONE;
        remainingEcho = 0;
        activateBranch = 0;
        rule=Rule.NM;
    }

    public TrackNode(String host) {
        type = Type.PROP;
        this.host = host;
        children = new ArrayList<TrackNode>();
        suspendedBranch = new Queue<TrackNode>();
        echo = Echo.NONE;
        remainingEcho = 0;
        activateBranch = 0;
        rule=Rule.NM;
    }

    public TrackNode(String enclosedWave, String suspendedTail, WaveEnvironment env){
        type = Type.PROP;
        children = new ArrayList<TrackNode>();
        suspendedBranch = new Queue<TrackNode>();
        echo = Echo.NONE;
        remainingEcho = 0;
        activateBranch = 0;
        this.enclosedWave = enclosedWave;
        this.suspendedTail = suspendedTail;
        this.waveEnv = env;
        rule=Rule.NM;
    }

    public TrackNode(String address, String enclosedWave, String suspendedTail, WaveEnvironment env){
        type = Type.PROP;
        children = new ArrayList<TrackNode>();
        suspendedBranch = new Queue<TrackNode>();
        echo = Echo.NONE;
        remainingEcho = 0;
        activateBranch = 0;
        this.enclosedWave = enclosedWave;
        this.suspendedTail = suspendedTail;
        this.waveEnv = env;
        this.address = address;
        this.waveEnv.setTrackAddress(address);
        rule=Rule.NM;
    }
    public TrackNode(String suspendedTail, WaveEnvironment env){
        type = Type.PROP;
        children = new ArrayList<TrackNode>();
        suspendedBranch = new Queue<TrackNode>();
        echo = Echo.NONE;
        remainingEcho = 0;
        activateBranch = 0;
        this.suspendedTail = suspendedTail;
        this.waveEnv = env;
        env.setTrackAddress(address);
        rule=Rule.NM;
    }

    public TrackNode(String suspendedTail, WaveEnvironment env, String host){
        type = Type.PROP;
        this.host = host;
        parent = null;
        children = new ArrayList<TrackNode>();
        suspendedBranch = new Queue<TrackNode>();
        echo = Echo.NONE;
         remainingEcho = 0;
        activateBranch = 0;
        this.suspendedTail = suspendedTail;
        this.waveEnv = env;
        env.setTrackAddress(address);
        rule=Rule.NM;
    }

    public Type getType() {
        return type;
    }

    public void setType(Type type) {
        this.type = type;
    }

    public String getEnclosedWave() {
        return enclosedWave;
    }

    public void setEnclosedWave(String wave) {
        this.enclosedWave = wave;
    }
    
    public String getAddress() {
        return address;
    }
    public void setAddress(String address) {
        this.address = address;
        if(waveEnv != null) waveEnv.setTrackAddress(address);
    }
    
    public String getHost() {
        return this.host;
    }
    public void setHost(String hostname){
        this.host = hostname;
    }
    public boolean isRemoteTrack(){        
        if (host == null) {
            return false;
        }
        return true;
    }
    

    public Rule getRule() {
        return rule;
    }

    public void setRule(Rule rule){
        this.rule = rule;
    }

    public int getActivateBranch() {
        return activateBranch;
    }

    public void setActivateBranch(int activateBranch) {
        this.activateBranch = activateBranch;
    }
    
    /**
     * Function returns Parent track node
     * @return
     */
    public TrackNode getParent(){
        return this.parent;
    }
    
    public void destroy(){
        if(parent != null){
            parent.children.remove(this);
        }
        Iterator<TrackNode> itr = children.iterator();
        while(itr.hasNext()){
            itr.next().setParent(null);
        }
    }
    /**
     * Function return array child tracks node
     * @return
     */
    public ArrayList<TrackNode> getAllChilds(){
        return this.children;
    }
    /**
     * Function returns number child tracks
     * @return
     */  
    
    public int getNumberChild(){
        return this.children.size();
    }
    
   
    /**
     * Function adds child into the parent track
     * @param child
     */
    public void addChild(TrackNode child){
        this.children.add(child);
        child.parent = this;
        remainingEcho++;
        ////////////////////////////////////////////
    }
    
    public void suspended(TrackNode branch){
        suspendedBranch.enqueue(branch);
    }
    public boolean hasSuspendedBranch(){
        return !suspendedBranch.isEmpty();
    }
    public TrackNode nextBranch(){
        return suspendedBranch.dequeue();
    }
    public TrackNode nextRandomBranch(){
        return suspendedBranch.randomDequeue();
    }
    public void deleteBranch(TrackNode child){
        this.children.remove(child);
        child.setParent(null);        
    }

    public int getRemainingEcho() {
        return  remainingEcho;
    }
    public void increaseRemainEcho(){
         remainingEcho++;
    }
    public void decreaseRemainingEcho(){
         remainingEcho--;
    }
    public TrackNode getChild(int index){
        return this.children.get(index);
    }
    /**
     * Function sets track parent
     * @param parent
     */
    public void setParent(TrackNode parent){
        //this.parent = parent;
        if(parent != null)
            parent.addChild(this);        
    }
    /**
     * Function sets result to a track node
     * @param result
     */
    public WaveEnvironment getWaveEnv(){
        return waveEnv;
    }
    
    public void suspendedTail(String tail){
        this.suspendedTail = tail;
    }

    public String getSuspendedTail() {
        return suspendedTail;
    }
    
    public void setWaveEnv(WaveEnvironment waveEnv) {
        this.waveEnv = waveEnv;
        if(waveEnv != null)  this.waveEnv.setTrackAddress(address);
    }
    public void setEcho(Echo echo){
        this.echo = echo;
    }
    public void mergeEcho(Echo recvEcho){
        if(this.echo == Echo.ABORT) return;
        //Echo message control by rule
        if(this.echo == Echo.NONE){
            this.echo = recvEcho;
            return;
        }
        if(recvEcho == Echo.ABORT){
            this.echo = recvEcho;
            return;
        }
        switch(rule){
            case NM:
            case SQ:
            case OS:
            case OP:
            case WT:
            case RP:
            case CR:
                if(echo == Echo.FALSE){
                    this.echo = recvEcho;
                } else if(echo == Echo.DONE){
                    if(recvEcho == Echo.TRUE)
                        this.echo = recvEcho;
                }                
                break;
            case AP:
            case AS:
                if(recvEcho.equals(Echo.FALSE) || this.echo.equals(Echo.FALSE))
                    this.echo = Echo.FALSE;
                else this.echo = recvEcho;
                break;
        }
    }

    public Echo getEcho(){
        return this.echo;
    }
    public boolean isFingerTrack(){
        return children.size() == 0;
    }
   
    @Override
    public String toString() {
        String nameParent = this.parent!= null? this.parent.address:"ROOT";
        return this.type + " " + this.address + " " + this.host + " " + this.enclosedWave + " " + this.suspendedTail +
                " " + this.echo +
                " " + nameParent + " " + this.rule;
    }
    
    public enum Echo{FALSE, DONE, TRUE, ABORT, NONE};
    public enum Rule{SQ, OS, AS, OP, AP, RP, WT, ID, NM, CR, RD, RL};
    public enum Type{PROP, SECT};
}
